Option Explicit

'################################################################
' Descriptor
' 数组矩阵的维度抽象
' 从 0 开始
' 假设维度级别的范围是 0 -- N，0 -- (N-1) 全部都可以是 Dimension 维度对象
' 但是第 N 级将会是保存的真实数据
'################################################################

'################################################################
' Field
'################################################################
' 父对象: 最外层的矩阵对象
Private originMatrix As ArrayMatrix
' 到达当前维度的索引路径(数组)
' int[]
Private dimensionIndexesPath As Variant
' 当前维度对象的所属的维度级别 (从 0 开始)
Private dimensionLevel As Integer

'################################################################
' Constructor
'################################################################
Private Sub Class_Initialize()
End Sub

Private Sub Class_Terminate()
End Sub

'################################################################
' Property
'################################################################
' 当前维度对象所在的维度级别
' @param originMatrixVal 父对象: 最外层的矩阵对象
' @param dimensionIndexesPathVal 到达当前维度的索引路径(数组)
Public Sub Init(ByRef originMatrixVal As ArrayMatrix, ByRef dimensionIndexesPathVal As Variant)
    Set originMatrix = originMatrixVal
    dimensionIndexesPath = dimensionIndexesPathVal
    dimensionLevel = UBound(dimensionIndexesPath)
End Sub

'################################################################
' Property
'################################################################
' 获取当前维度对象所在的维度级别
' 从 0 开始
' 对于 0 - N 级的维度，最多只能获取到 N-1 级，因为第 N 级是真实数据
Public Property Get CurrentDimensionLevel() As Integer
    CurrentDimensionLevel = dimensionLevel
End Property

' 获取当前维度对象所在维度的长度
Public Property Get CurrentDimensionSize() As Integer
    CurrentDimensionSize = originMatrix.getDimensionLevelSize(dimensionLevel)
End Property

' 下一个维度的长度
Public Property Get NextDimensionSize() As Integer
    NextDimensionSize = originMatrix.getDimensionLevelSize(dimensionLevel + 1)
End Property

' 当前维度内可以包含的数据长度
' 实际上和下一个维度的长度 NextDimensionSize 是相同的
Public Property Get DataSize() As Integer
    DataSize = NextDimensionSize
End Property

' 返回当前维度对象所属的矩阵对象
Public Property Get ParentMatrix() As ArrayMatrix
    Set ParentMatrix = originMatrix
End Property

' 返回是否还有下一个维度对象。如果没有了，说明下一级是真实数据
' 对于一个维度级别为 0-N 级的矩阵，0-(N-2) 级将会返回 True，N-1 级将会返回 False
' 第 N 级是真实数据，不会产生维度对象
Public Property Get HasNextDimensionObject() As Boolean
    ' index    0 1 2 3
    '          | | |
    ' return   1 2 Nop
    ' 最多只有倒数第 3 维有下一维度对象，所以向前移动 2 位
    HasNextDimensionObject = dimensionLevel <= (originMatrix.DimensionCount - 3)
End Property

'################################################################
' Function
'################################################################
' 获取下一个维度的维度对象
' 对于 0 - N 的维度，如果当前维度是 N - 1，将会抛出异常
' 因为下一级第 N 级是真实数据，不需要维度对象，可以直接通过当前维度对象来获取数据
' @param nextDimensionIndex 下一个维度的索引
' @return 下一个维度的 ArrayMatrixDimension 对象
'         对于 0 - N 级的维度，最多返回 N-1 级。第 N 级是真实数据将不会创建维度对象，如果尝试获取将会抛出异常 ！！！！
'         如果维度索引的范围超过了维度的长度或者小于 0，将会抛出异常 ！！！！
Public Function getNextDimension(ByRef nextDimensionIndex As Integer) As ArrayMatrixDimension
    If HasNextDimensionObject = True Then
        ' 1. 检查维度范围
        If nextDimensionIndex < 0 Then
            Err.Raise 20003, Description:="ArrayMatrixDimension_getNextDimension: nextDimensionIndex[" & nextDimensionIndex & "] is less 0, can not get next dimension"
            Exit Function
        End If
        If nextDimensionIndex >= NextDimensionSize  Then
            Err.Raise 20004, Description:="ArrayMatrixDimension_getNextDimension: nextDimensionIndex[" & nextDimensionIndex & "] is great than max index[" & (NextDimensionSize - 1) & "], can not get next dimension"
            Exit Function
        End If

        ' 2. 为下一个维度创建索引路径
        Dim nextDPath As Variant
        nextDPath = UtilsArray.clone1DArrayAndMergeForParams( _
            dimensionIndexesPath, nextDimensionIndex)

        ' 3. 完成初始化
        Dim result As ArrayMatrixDimension
        Set result = New ArrayMatrixDimension
        result.Init originMatrix, nextDPath

        Set getNextDimension = result
    Else
        ' 如果下一级是真实数据，则抛出异常
        Err.Raise 20002, Description:="ArrayMatrixDimension_getNextDimension: matrix dimension is [" & originMatrix.DimensionCount & "], level of current dimension is [" & dimensionLevel & "], can not get next dimension, because next dimension is real data"
    End If
End Function

' 按照指定的各个维度级别的索引来获取维度对象
' @param nextDimensionIndexes 下一级及其以后的索引
' @return 下 nextDimensionIndexes 个维度的 ArrayMatrixDimension 对象
'         对于 0 - N 级的维度，最多返回 N-1 级。第 N 级是真实数据将不会创建维度对象，如果尝试获取将会抛出异常 ！！！！
'         如果维度索引的范围超过了维度的长度或者小于 0，将会抛出异常 ！！！！
'
'         如果没有指定任何参数，将直接返回当前对象本身
Public Function getSubDimensionByParams(ParamArray nextDimensionIndexes() As Variant) As ArrayMatrixDimension
    Dim indexSize As Integer
    indexSize = UBound(nextDimensionIndexes) + 1

    ' 1. 如果没有指定任何参数，将直接返回当前对象本身
    If indexSize = 0 Then
        Set getSubDimensionByParams = Me
        Exit Function
    End If

    ' 2. 检查维度数量，如果已经到了 N 级，则抛出异常
    ' 2.1 计算已经得到的维度数量
    Dim dLevelIndex As Integer
    dLevelIndex = dimensionLevel + indexSize

    ' 2.2 检查维度数量，如果已经到了 N 级，则抛出异常
    If dLevelIndex >= originMatrix.DimensionCount - 1 Then
        Err.Raise 20005, Description:="ArrayMatrixDimension_getSubDimensionByParams: matrix dimension count is [" & originMatrix.DimensionCount & "], level of excepted dimension is [" & (dLevelIndex + 1) & "], can not get next dimension, because next dimension is real data or has exceeded the maximun level" & _
                        vbCrLf & _
                        "param[nextDimensionIndexes]=" & UtilsArray.joinElemForParamsVal(nextDimensionIndexes, ",") & _
                        vbCrLf & _
                        "current dimensionIndexesPath=" & UtilsArray.joinElem(dimensionIndexesPath, ",")
        Exit Function
    End If

    ' 3. 检查 nextDimensionIndexes 参数中各维度索引的是否在正确的范围内，如果超出了范围需要抛出异常
    Dim i As Integer
    Dim curLevel As Integer
    curLevel = dimensionLevel + 1
    For i = 0 To indexSize - 1
        If nextDimensionIndexes(i) < 0 Then
            Err.Raise 20006, Description:="ArrayMatrixDimension_getSubDimensionByParams: nextDimensionIndexes[" & i & "]=" & nextDimensionIndexes(i) & _
                ", is less 0, can not get next dimension"
            Exit Function
        End If

        If nextDimensionIndexes(i) >= originMatrix.getDimensionLevelSize(curLevel) Then
            Err.Raise 20007, Description:="ArrayMatrixDimension_getSubDimensionByParams: nextDimensionIndexes[" & i & "]=" & nextDimensionIndexes(i) & _
                ", is great than max index[" & (originMatrix.getDimensionLevelSize(curLevel) - 1) & "], can not get next dimension"
            Exit Function
        End If

        curLevel = curLevel + 1
    Next

    ' 4. 创建指定的维度对象
    Dim nextDPath As Variant
    nextDPath = UtilsArray.clone1DArrayAndMergeForParamsVal( _
                        dimensionIndexesPath, nextDimensionIndexes)

    Dim result As ArrayMatrixDimension
    Set result = New ArrayMatrixDimension
    result.Init originMatrix, nextDPath

    Set getSubDimensionByParams = result
End Function

' 获取指定索引的数据
' 对于 0 - N 级的维度，假设当前维度对象所在的维度是 X，
' 参数 nextDimensionIndexes 必须传入从 X + 1 -- N 级每一集的维度索引，否则将会抛出异常
' 如果各维度的索引值超过的维度要求的范围，将会抛出异常
' @param nextDimensionIndexes 从下一级开始直到第 N 级的所有维度索引
' @return 指定位置上的数据
Public Function getData(ParamArray nextDimensionIndexes() As Variant) As Variant
    ' 1. 检查维度数量，如果没有到达 N 级，则抛出异常
    ' 1.1 计算已经得到的维度数量
    Dim indexSize As Integer
    indexSize = UBound(nextDimensionIndexes) + 1

    Dim dLevelIndex As Integer
    dLevelIndex = dimensionLevel + indexSize

    If dLevelIndex <> originMatrix.DimensionCount - 1 Then
        Err.Raise 20008, Description:="ArrayMatrixDimension_getData: matrix dimension count is [" & originMatrix.DimensionCount & "], level of excepted dimension is [" & (dLevelIndex + 1) & "], can not get real data." & _
                        vbCrLf & _
                        "param[nextDimensionIndexes]=" & UtilsArray.joinElemForParamsVal(nextDimensionIndexes, ",") & _
                        vbCrLf & _
                        "current dimensionIndexesPath=" & UtilsArray.joinElem(dimensionIndexesPath, ",")
        Exit Function
    End If

    ' 2. 检查 indexes 参数中各维度索引的是否在正确的范围内，如果超出了范围需要抛出异常
    Dim i As Integer
    Dim curLevel As Integer
    curLevel = dimensionLevel + 1
    For i = 0 To indexSize - 1
        If nextDimensionIndexes(i) < 0 Then
            Err.Raise 20006, Description:="ArrayMatrixDimension_getData: nextDimensionIndexes[" & i & "]=" & nextDimensionIndexes(i) & _
                ", is less 0, can not get next dimension"
            Exit Function
        End If

        If nextDimensionIndexes(i) >= originMatrix.getDimensionLevelSize(curLevel) Then
            Err.Raise 20006, Description:="ArrayMatrixDimension_getData: nextDimensionIndexes[" & i & "]=" & nextDimensionIndexes(i) & _
                ", is great than max index[" & (originMatrix.getDimensionLevelSize(curLevel) - 1) & "], can not get next dimension"
            Exit Function
        End If

        curLevel = curLevel + 1
    Next

    ' 3. 获取数据
On Error GoTo catch:
    getData = originMatrix.inner_getData(dimensionIndexesPath, nextDimensionIndexes)
    Exit Function
catch:
    Set getData = originMatrix.inner_getData(dimensionIndexesPath, nextDimensionIndexes)
End Function

' 从指定索引处获取基础类型的数据
' 对于 0 - N 级的维度，假设当前维度对象所在的维度是 X，
' 参数 nextDimensionIndexes 必须传入从 X + 1 -- N 级每一集的维度索引，否则将会抛出异常
' 如果各维度的索引值超过的维度要求的范围，将会抛出异常
' @param nextDimensionIndexes 从下一级开始直到第 N 级的所有维度索引
' @return 指定位置上的数据
Public Function getPrimitiveData(ParamArray nextDimensionIndexes() As Variant) As Variant
    ' 1. 检查维度数量，如果没有到达 N 级，则抛出异常
    ' 1.1 计算已经得到的维度数量
    Dim indexSize As Integer
    indexSize = UBound(nextDimensionIndexes) + 1

    Dim dLevelIndex As Integer
    dLevelIndex = dimensionLevel + indexSize

    If dLevelIndex <> originMatrix.DimensionCount - 1 Then
        Err.Raise 20008, Description:="ArrayMatrixDimension_getPrimitiveData: matrix dimension count is [" & originMatrix.DimensionCount & "], level of excepted dimension is [" & (dLevelIndex + 1) & "], can not get real data." & _
                        vbCrLf & _
                        "param[nextDimensionIndexes]=" & UtilsArray.joinElemForParamsVal(nextDimensionIndexes, ",") & _
                        vbCrLf & _
                        "current dimensionIndexesPath=" & UtilsArray.joinElem(dimensionIndexesPath, ",")
        Exit Function
    End If

    ' 2. 检查 indexes 参数中各维度索引的是否在正确的范围内，如果超出了范围需要抛出异常
    Dim i As Integer
    Dim curLevel As Integer
    curLevel = dimensionLevel + 1
    For i = 0 To indexSize - 1
        If nextDimensionIndexes(i) < 0 Then
            Err.Raise 20006, Description:="ArrayMatrixDimension_getPrimitiveData: nextDimensionIndexes[" & i & "]=" & nextDimensionIndexes(i) & _
                ", is less 0, can not get next dimension"
            Exit Function
        End If

        If nextDimensionIndexes(i) >= originMatrix.getDimensionLevelSize(curLevel) Then
            Err.Raise 20006, Description:="ArrayMatrixDimension_getPrimitiveData: nextDimensionIndexes[" & i & "]=" & nextDimensionIndexes(i) & _
                ", is great than max index[" & (originMatrix.getDimensionLevelSize(curLevel) - 1) & "], can not get next dimension"
            Exit Function
        End If

        curLevel = curLevel + 1
    Next

    ' 3. 获取数据
    getPrimitiveData = originMatrix.inner_getPrimitiveData(dimensionIndexesPath, nextDimensionIndexes)
End Function

' 从指定索引处获取对象类型的数据
' 对于 0 - N 级的维度，假设当前维度对象所在的维度是 X，
' 参数 nextDimensionIndexes 必须传入从 X + 1 -- N 级每一集的维度索引，否则将会抛出异常
' 如果各维度的索引值超过的维度要求的范围，将会抛出异常
' @param nextDimensionIndexes 从下一级开始直到第 N 级的所有维度索引
' @return 指定位置上的数据
Public Function getObjData(ParamArray nextDimensionIndexes() As Variant) As Variant
    ' 1. 检查维度数量，如果没有到达 N 级，则抛出异常
    ' 1.1 计算已经得到的维度数量
    Dim indexSize As Integer
    indexSize = UBound(nextDimensionIndexes) + 1

    Dim dLevelIndex As Integer
    dLevelIndex = dimensionLevel + indexSize

    If dLevelIndex <> originMatrix.DimensionCount - 1 Then
        Err.Raise 20008, Description:="ArrayMatrixDimension_getObjData: matrix dimension count is [" & originMatrix.DimensionCount & "], level of excepted dimension is [" & (dLevelIndex + 1) & "], can not get real data." & _
                        vbCrLf & _
                        "param[nextDimensionIndexes]=" & UtilsArray.joinElemForParamsVal(nextDimensionIndexes, ",") & _
                        vbCrLf & _
                        "current dimensionIndexesPath=" & UtilsArray.joinElem(dimensionIndexesPath, ",")
        Exit Function
    End If

    ' 2. 检查 indexes 参数中各维度索引的是否在正确的范围内，如果超出了范围需要抛出异常
    Dim i As Integer
    Dim curLevel As Integer
    curLevel = dimensionLevel + 1
    For i = 0 To indexSize - 1
        If nextDimensionIndexes(i) < 0 Then
            Err.Raise 20006, Description:="ArrayMatrixDimension_getObjData: nextDimensionIndexes[" & i & "]=" & nextDimensionIndexes(i) & _
                ", is less 0, can not get next dimension"
            Exit Function
        End If

        If nextDimensionIndexes(i) >= originMatrix.getDimensionLevelSize(curLevel) Then
            Err.Raise 20006, Description:="ArrayMatrixDimension_getObjData: nextDimensionIndexes[" & i & "]=" & nextDimensionIndexes(i) & _
                ", is great than max index[" & (originMatrix.getDimensionLevelSize(curLevel) - 1) & "], can not get next dimension"
            Exit Function
        End If

        curLevel = curLevel + 1
    Next

    ' 3. 获取数据
    Set getObjData = originMatrix.inner_getObjData(dimensionIndexesPath, nextDimensionIndexes)
End Function

' 设置指定索引的数据
' 对于 0 - N 级的维度，假设当前维度对象所在的维度是 X，
' 参数 nextDimensionIndexes 必须传入从 X + 1 -- N 级每一集的维度索引，否则将会抛出异常
' 如果各维度的索引值超过的维度要求的范围，将会抛出异常
' @param val 需要设置的数据
' @param nextDimensionIndexes 从下一级开始直到第 N 级的所有维度索引
Public Sub setData(ByRef val As Variant, ParamArray nextDimensionIndexes() As Variant)
    ' 1. 检查维度数量，如果没有到达 N 级，则抛出异常
    ' 1.1 计算已经得到的维度数量
    Dim indexSize As Integer
    indexSize = UBound(nextDimensionIndexes) + 1

    Dim dLevelIndex As Integer
    dLevelIndex = dimensionLevel + indexSize

    If dLevelIndex <> originMatrix.DimensionCount - 1 Then
        Err.Raise 20008, Description:="ArrayMatrixDimension_setData: matrix dimension count is [" & originMatrix.DimensionCount & "], level of excepted dimension is [" & (dLevelIndex + 1) & "], can not get real data." & _
                        vbCrLf & _
                        "param[nextDimensionIndexes]=" & UtilsArray.joinElemForParamsVal(nextDimensionIndexes, ",") & _
                        vbCrLf & _
                        "current dimensionIndexesPath=" & UtilsArray.joinElem(dimensionIndexesPath, ",")
        Exit Sub
    End If

    ' 2. 检查 indexes 参数中各维度索引的是否在正确的范围内，如果超出了范围需要抛出异常
    Dim i As Integer
    Dim curLevel As Integer
    curLevel = dimensionLevel + 1
    For i = 0 To indexSize - 1
        If nextDimensionIndexes(i) < 0 Then
            Err.Raise 20006, Description:="ArrayMatrixDimension_setData: nextDimensionIndexes[" & i & "]=" & nextDimensionIndexes(i) & _
                ", is less 0, can not get next dimension"
            Exit Sub
        End If

        If nextDimensionIndexes(i) >= originMatrix.getDimensionLevelSize(curLevel) Then
            Err.Raise 20006, Description:="ArrayMatrixDimension_setData: nextDimensionIndexes[" & i & "]=" & nextDimensionIndexes(i) & _
                ", is great than max index[" & (originMatrix.getDimensionLevelSize(curLevel) - 1) & "], can not get next dimension"
            Exit Sub
        End If

        curLevel = curLevel + 1
    Next

    ' 3. 设置数据
    originMatrix.inner_setData val, dimensionIndexesPath, nextDimensionIndexes
End Sub
